package com.carry.common.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneOffset;
import java.util.Date;

/**
 * 时间工具类
 * 
 * @author CARRY ME HOME 2020-7-3017:26:22
 */
public class DateTime {

	/**
	 * <p>
	 * 基于现在日期判断日期是否在 指定的日期段内，如在一年内，在一个月之内，在一年零两个月之内。 以具体的时间值计算，比如算一个月之内等价于30天之内。
	 * </p>
	 * 注意：没有做入参合法性校验，可能存在bug
	 * 
	 * @param date  需要判断的日期
	 * @param year  年
	 * @param month 月
	 * @param day   日
	 * @return
	 */
	public static boolean isWithin(Date date, int year, int month, int day, int week) {
		if(date == null) {
			return false;
		}
		
		LocalDate ld = LocalDate.now();

		Instant i = date.toInstant();
		Instant a = ld.minusYears(year)
				.minusMonths(month)
				.minusDays(day)
				.minusDays(1)
				.minusWeeks(week)
				.atTime(LocalTime.MAX)
				.toInstant(ZoneOffset.UTC);

		Instant b = ld.atStartOfDay()
				.toInstant(ZoneOffset.UTC);

		if (i.isAfter(a) && i.isBefore(b)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 计算日期是否在某个日期段内。
	 * <p>
	 * 比如计算date是否在一个月以内，则传入month=1，其他参数为0。如果 date处于上个月的一号至now()以内，则返回true。
	 * </p>
	 * 
	 * <p>
	 * 注意：<br>
	 * 1、一次调用只能计算一种日期，比如计算月份的时候传入月份的值，其他的参数必须为0。<br>
	 * 2、参数全部为0时，date介于今天的00:00:00和now()之间则返回true
	 * </p>
	 * 
	 */
	private static boolean judge(Date date, int year, int month, int day, int week) {
		if(date == null) {
			return false;
		}
		
		LocalDate nd = LocalDate.now();
		Month mm = nd.getMonth();

		if (year != 0) {
			year--;
			nd = LocalDate.of(nd.getYear(), 1, 1).minusYears(year);
		}

		if (month != 0) {
			month--;
			nd = LocalDate.of(nd.getYear(), mm, 1).minusMonths(month);
		}

		if (day != 0) {
			day--;
			nd = nd.minusDays(day);
		}

		if (week != 0) {
			week--;
			int i = nd.getDayOfWeek().getValue() - 1;
			nd = nd.minusDays(i).minusWeeks(week);
		}

		Instant i = date.toInstant();
		Instant b = LocalDateTime.of(nd, LocalTime.MIN).toInstant(ZoneOffset.UTC);
		
		return i.isAfter(b) && i.isBefore(Instant.now()) ? true : false;
	}

	/**
	 * <p>
	 * 判断日期是否在year年内，year=1时判断date是否为今年的日期。
	 * </p>
	 * 
	 * EX：<br>
	 * 判断date是否是两年内的日期，withinYear(date, 2)
	 * <p>
	 * 如果date介于去年的1月1日00:00:00和now()之间则返回true
	 * </p>
	 * 
	 * @param date 需要进行判断的日期
	 * @param year 传入1则判断是否为本年，传入其他则是当前年份减去1
	 */
	public static boolean withinYear(Date date, int year) {
		return judge(date, year, 0, 0, 0);
	}

	/**
	 * <p>
	 * 判断日期是否在month月内，month=1时判断date是否为本月的日期。
	 * </p>
	 * <p>
	 * 使用方法与withinYear类似
	 * </p>
	 * 
	 * @see DateTime#withinYear(Date, int)
	 */
	public static boolean withinMonth(Date date, int month) {
		return judge(date, 0, month, 0, 0);
	}

	/**
	 * <p>
	 * 判断日期是否在day天内，day=1时判断date是否为今天的日期。
	 * </p>
	 * <p>
	 * 使用方法与withinYear类似
	 * </p>
	 * 
	 * @see DateTime#withinYear(Date, int)
	 */
	public static boolean withinDay(Date date, int day) {
		return judge(date, 0, 0, day, 0);
	}

	/**
	 * <p>
	 * 判断日期是否在week星期内，week=1时判断date是否为本星期的日期。
	 * </p>
	 * <p>
	 * 使用方法与withinYear类似
	 * </p>
	 * 
	 * @see DateTime#withinYear(Date, int)
	 */
	public static boolean withinWeek(Date date, int week) {
		return isWithin(date, 0, 0, 0, week);
	}

}
